package com.magnificent.atlantis.client.event;

import com.magnificent.atlantis.client.data.user.UserContext;
import com.magnificent.commons.utils.collections.CollectionsUtils;
import com.magnificent.commons.utils.other.Pair;

import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * User: Babanin
 * Date: 15.02.2009
 * Time: 19:47:06
 */
public class EventDispatcher extends Thread {

    private static Map<UserContext, EventDispatcher> instance;
    private Map<EventType, List<EventDispatcherListener>> eventToListener;

    private Queue<Pair<EventType, Object>> eventQueue;

    static {
        instance = new HashMap<UserContext, EventDispatcher>();

        initializeEventDispatcherInstance(UserContext.DEFAULT);
    }

    public static EventDispatcher getInstance() {
        return instance.get(UserContext.DEFAULT);
    }

    public static EventDispatcher getInstance(UserContext userContext) {
        if (!instance.containsKey(userContext)) {
            initializeEventDispatcherInstance(userContext);
        }

        return instance.get(userContext);
    }

    private static void initializeEventDispatcherInstance(UserContext userContext) {
        EventDispatcher eventDispatcher = new EventDispatcher();
        instance.put(userContext, eventDispatcher);

        for (EventType eventType : EventType.values()) {
            eventDispatcher.eventToListener.put(eventType, new ArrayList<EventDispatcherListener>());
        }

        eventDispatcher.start();
    }

    @Override
    public void run() {
        while (true) {
            if (!eventQueue.isEmpty()) {
                Pair<EventType, Object> eventTypeObjectPair = eventQueue.poll();

                Object obj = eventTypeObjectPair.getSecond();
                EventType eventType = eventTypeObjectPair.getFirst();

                List<EventDispatcherListener> dispatcherListeners = eventToListener.get(eventType);
                for (EventDispatcherListener listener : dispatcherListeners) {
                    listener.newEvent(eventType, obj);
                }
            }
        }
    }

    public EventDispatcher() {
        eventQueue = new ConcurrentLinkedQueue<Pair<EventType, Object>>();
        eventToListener = new HashMap<EventType, List<EventDispatcherListener>>();
    }

    public void addListener(EventDispatcherListener listener) {
        for (EventType eventType : eventToListener.keySet()) {
            addListener(eventType, listener);
        }
    }

    public void addListener(List<EventType> eventTypes, EventDispatcherListener listener) {
        for (EventType eventType : eventTypes) {
            addListener(eventType, listener);
        }
    }

    public void addListener(EventType[] eventTypes, EventDispatcherListener listener) {
        addListener(CollectionsUtils.list(eventTypes), listener);
    }

    public void addListener(EventType eventType, EventDispatcherListener listener) {
        eventToListener.get(eventType).add(listener);
    }

    public void removeListener(EventType eventType, EventDispatcherListener listener) {
        List<EventDispatcherListener> dispatcherListeners = eventToListener.get(eventType);
        if (dispatcherListeners != null) {
            dispatcherListeners.remove(listener);
        }
    }

    public synchronized void postEvent(EventType eventType, Object obj) {
        eventQueue.add(new Pair<EventType, Object>(eventType, obj));
    }
}
